/*This is a test to push to the server
 -Abebe Woreta*/

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;




namespace HelloWorldGame
{

    public class HelloWorldGame : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager m_graphics; 
        SpriteBatch m_spriteBatch;

        const int MOVE_CONSTANT = 35;
        const int SPIN_CONSTANT = 0;
        Random m_rand = new Random(); 

        /// List of all the game objects which exist - used to update/draw them all.
        List<GameThing> m_allThings = new List<GameThing>();
        /// A Special game object which the player controls. Must also be a member of m_allThings.
        GameThing m_playerThing;
        
        /// Gravity
        Vector2 m_gravity = new Vector2(0f,0.2f);

        /// Bounce Multiplier Vectors
        Vector2 m_bounceX = new Vector2(-0.85f, 0.85f); // Use 0.8 to slow down a bit when bouncing.
        Vector2 m_bounceY = new Vector2(0.85f, -0.85f);

        /// Players score and life values.
        int m_score = 0;
        int m_life = 100;

        /// Dictionary of events which can 
        //Dictionary<String, float> lastEventTime = new Dictionary<String, float>();

        /// Fixed positions on the screen, used for various things.
        Dictionary<String, Vector2> m_fixedPositions = new Dictionary<string, Vector2>();
        /// Textures, used for drawing. 
        Dictionary<String, Texture2D> m_textures = new Dictionary<string, Texture2D>();
        /// A basic font for drawing labels.  
        SpriteFont m_basicFont;

        /// Used for mouse controls.
        MouseState m_lastMouseState = new MouseState();

        /// Dictionary used by KeyJustPressed.
        Dictionary<Keys, Boolean> m_keyLastState = new Dictionary<Keys, Boolean>();
        /// ----------------------------------------------------------------------
        /// A utility function which lets us do something only once per key press. 
        bool KeyJustPressed(Keys key)
        {
            bool retVal = false;
            if (Keyboard.GetState().IsKeyDown(key))
            {
                if (m_keyLastState[key] == false)
                {
                    retVal = true;
                }
                m_keyLastState[key] = true;
            }
            else
            {
                m_keyLastState[key] = false;
            }
            return retVal;
        }



        /// ----------------------------------------------------------------------
        public HelloWorldGame()
        {
            m_graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            m_graphics.PreferredBackBufferWidth = 800;
            m_graphics.PreferredBackBufferHeight = 600;
        }


        /// ----------------------------------------------------------------------
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            //this.IsMouseVisible = true;

            base.Initialize();
        }


        /// ----------------------------------------------------------------------
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            m_spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load Textures
            m_textures["Robot1"] = Content.Load<Texture2D>("Robot1");
            m_textures["Money"] = Content.Load<Texture2D>("Money");
            m_textures["GoldBar"] = Content.Load<Texture2D>("GoldBar");
            m_textures["Bomb"] = Content.Load<Texture2D>("Bomb");
            m_textures["Cursor"] = Content.Load<Texture2D>("Cursor");
            
            // Load Font
            m_basicFont = Content.Load<SpriteFont>("basicFont");

            // Set some fixed positions 
            m_fixedPositions["Score"] = new Vector2(10, 10);
            m_fixedPositions["Health"] = new Vector2(m_graphics.GraphicsDevice.Viewport.Width - 100, 10);
            m_fixedPositions["Controls"] = new Vector2(0, 60);


            // Create the player's sprite
            m_allThings.Add(new GameThing(m_textures["Robot1"], Vector2.Zero, new Vector2(16,8), 0, 0, Color.White));
            m_playerThing = m_allThings[0];

        }


        /// ----------------------------------------------------------------------
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }



        /// ----------------------------------------------------------------------
        protected override void Update(GameTime gameTime)
        {

            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            // Stop updating if the game is over.
            if (m_life <= 0)
            {
                m_playerThing.Tint = Color.Red; //Also Tint the player object red.
                return;
            }

            // Get the time elapsed as a float:
            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
            float elapsedMove = (float)elapsedTime * MOVE_CONSTANT;
            float elapsedSpin = (float)elapsedTime * SPIN_CONSTANT;
            float totalTime = (float)gameTime.TotalGameTime.TotalSeconds;


            // Get mouse data:
            MouseState currentMouseState = Mouse.GetState();
            bool mouseLeftClick = (currentMouseState.LeftButton == ButtonState.Pressed && 
                m_lastMouseState.LeftButton == ButtonState.Released);
            bool mouseLeftRelease = (m_lastMouseState.LeftButton == ButtonState.Pressed &&
                currentMouseState.LeftButton == ButtonState.Released);
            Point mousePos = new Point(currentMouseState.X, currentMouseState.Y);
            //Finished with mouse input; save last state.
            m_lastMouseState = currentMouseState;


            // Get Keyboard Input:
            if (Keyboard.GetState().IsKeyDown(Keys.Up)) //player up
                m_playerThing.Speed.Y -= elapsedMove; 
            if (Keyboard.GetState().IsKeyDown(Keys.Down)) // player down
                m_playerThing.Speed.Y += elapsedMove;  
            if (Keyboard.GetState().IsKeyDown(Keys.Left)) //player left
                m_playerThing.Speed.X -= elapsedMove;
            if (Keyboard.GetState().IsKeyDown(Keys.Right)) // player right
                m_playerThing.Speed.X += elapsedMove;
            if (Keyboard.GetState().IsKeyDown(Keys.W)) //gravity up
                m_gravity.Y -= 0.1f * elapsedMove;
            if (Keyboard.GetState().IsKeyDown(Keys.S)) // gravity down
                m_gravity.Y += 0.1f * elapsedMove;
            if (Keyboard.GetState().IsKeyDown(Keys.A)) // gravity left
                m_gravity.X -= 0.1f * elapsedMove;
            if (Keyboard.GetState().IsKeyDown(Keys.D)) // gravity right
                m_gravity.X += 0.1f * elapsedMove;
            if (Keyboard.GetState().IsKeyDown(Keys.X)) //zero gravity 
                m_gravity = Vector2.Zero;
            if (Keyboard.GetState().IsKeyDown(Keys.Space)) // stop the player-thing.
                m_playerThing.Speed = Vector2.Zero;


            float moneyFrequency = 1.7f;
            if ((totalTime % moneyFrequency) > ((totalTime + elapsedTime) % moneyFrequency) 
               || KeyJustPressed(Keys.M) ) // Hackish way to get it to happen every so often. :)
            {
                GameThing money = new GameThing(
                    m_textures["Money"],
                    new Vector2(m_rand.Next(0,m_graphics.GraphicsDevice.Viewport.Width),0), // Random point at top of screen.  
                    new Vector2(m_rand.Next(-8, 8), m_rand.Next(-4, 4)),                    // Random speed/direction. 
                    5,  // 5 point for money
                    0,  // no damage
                    Color.White,
                    (float)m_rand.Next(5, 20) * 0.1f);   //Random size.
                m_allThings.Add(money);
            }

            float goldFrequency = 5f;
            if ( (totalTime % goldFrequency) > ((totalTime + elapsedTime) % goldFrequency) ||
               KeyJustPressed(Keys.G) ) // Hackish way to get it to happen every so often. :)
            {
                GameThing gold = new GameThing(
                    m_textures["GoldBar"], 
                    new Vector2(m_rand.Next(0,m_graphics.GraphicsDevice.Viewport.Width),0), // Random point at top of screen.
                    new Vector2(m_rand.Next(-8, 8), m_rand.Next(-5, 5)),                    // Random speed/direction. 
                    25,    // 25 base points for gold
                    0,    // no damage
                    Color.White,
                    (float)m_rand.Next(5, 20) * 0.1f);   //Random size.
                m_allThings.Add(gold);
            }

            float bombFrequency = 0.8f + (5f / (1 + totalTime/16));
            if (KeyJustPressed(Keys.B) ||
               (totalTime % bombFrequency) > ((totalTime + elapsedTime) % bombFrequency)) // Hackish way to get it to happen every so often. :)
            {
                
                GameThing money = new GameThing(
                    m_textures["Bomb"],
                    new Vector2(m_rand.Next(0, m_graphics.GraphicsDevice.Viewport.Width), 0),// Random point at top of screen.
                    new Vector2(m_rand.Next(-6, 6), m_rand.Next(-5, 5)),                    // Random speed/direction.
                    0,    // No points for a bomb
                    10,    // 10 base damage
                    Color.White,
                    (float)m_rand.Next(5, 20) * 0.1f);  //Random size.
                m_allThings.Add(money);
            }



            for(int ii = 0; ii < m_allThings.Count; ii++)
            {
                GameThing thing = m_allThings[ii];

                // Gravity adds to speed
                thing.Speed += m_gravity * elapsedMove;

                // Speed adds to location
                thing.Pos += thing.Speed * elapsedMove;

                // Spin adds to rotation
                thing.Rotation += thing.Spin * elapsedSpin;
                thing.Rotation = thing.Rotation % (MathHelper.Pi * 2);
                

                // Is this thing touching a wall? Bounce off!
                int MaxX = m_graphics.GraphicsDevice.Viewport.Width - thing.Texture.Width/2;
                int MinX = thing.Texture.Width / 2;
                int MaxY = m_graphics.GraphicsDevice.Viewport.Height - thing.Texture.Height/2;
                int MinY = thing.Texture.Height / 2;

                if (thing.Pos.X > MaxX)
                {
                    thing.Speed *= m_bounceX; 
                    thing.Pos.X = MaxX;
                }
                if (thing.Pos.X < MinX)
                {
                    thing.Speed *= m_bounceX;
                    thing.Pos.X = MinX;
                }
                if (thing.Pos.Y > MaxY)
                {
                    thing.Speed *= m_bounceY;
                    thing.Pos.Y = MaxY;
                }
                if (thing.Pos.Y < MinY)
                {
                    thing.Speed *= m_bounceY;
                    thing.Pos.Y = MinY;
                }


                // What if this thing is touching the player thing?  It gets collected!
                if (thing != m_playerThing && thing.IsTouching(m_playerThing))
                {

                    // use scale: bigger things give more points and damage!
                    m_score += (int)(thing.ScoreOnCollect * thing.Scale); 
                    m_life -= (int)(thing.DamageOnTouch * thing.Scale);
                    
                    // Remove the thing that got collected.
                    m_allThings.RemoveAt(ii);
                    ii--; // Gotta correct for removing an element from the list. 
                    continue;
                }


                // What if the mouse went over the thing?
                if (thing.IsTouching(mousePos))
                {
                    if (mouseLeftRelease && thing != m_playerThing)
                    {  // Remove the thing if the mouse is released over it.
                        m_allThings.RemoveAt(ii);
                        ii--; // Gotta correct for removing an element from the list.  
                        continue;
                    }
                    if (mouseLeftClick)
                    {  //Stop the thing if it's clicked on.  
                        thing.Speed = Vector2.Zero;
                    }

                    thing.Tint = Color.DeepPink;
                }

            }

            base.Update(gameTime);
        }


        /// ----------------------------------------------------------------------
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            //Start the "batch".  
            m_spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);

            //Draw the score and health labels first.
            m_spriteBatch.DrawString(m_basicFont, "Move:arrows&space  Gravity:WASD,X  Spawn:M,G,B", m_fixedPositions["Controls"], Color.White);
            m_spriteBatch.DrawString(m_basicFont, "Score: " + m_score.ToString(), m_fixedPositions["Score"], Color.White);
            m_spriteBatch.DrawString(m_basicFont, "HP: " + m_life.ToString(), m_fixedPositions["Health"], Color.White);

            m_spriteBatch.Draw(m_textures["Cursor"], new Vector2(m_lastMouseState.X, m_lastMouseState.Y), Color.White);

            for(int ii = 0; ii < m_allThings.Count; ii++)
            {
                GameThing thing = m_allThings[ii];

                // The origin for the ratation we'll apply - we use the center of the sprite.
                Vector2 origin = new Vector2(thing.Texture.Width/2, thing.Texture.Height/2);

                m_spriteBatch.Draw(
                    thing.Texture,      // The texture 
                    thing.Pos,          // The location
                    null, thing.Tint,   // Draw the whole taxture, in the given tint.
                    thing.Rotation,     // The rotation to apply
                    origin,             // The origin of rotation calculated above 
                    thing.Scale,        // The scale
                    SpriteEffects.None, // No effects for now
                    0f);                // Layers are unsorted for now - all zero.
            }

            // Finish the batch
            m_spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
